package fun.xming.memlib.tools;
import android.os.Environment;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.StringWriter;
import java.util.Map;
import java.util.HashMap;
import java.io.File;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import fun.xming.memlib.MainActivity;
import com.google.gson.internal.LinkedTreeMap;

public class MemManager {
    
    // 真实位置【/mnt/sdcard/Android/data/<package_name>/files/Movies】
// 模拟位置【/storage/emulated/0/Android/data/<package_name>/files/Movies】
	private String pathExternalFiles = "";
	private HashMap<String,String> contents;
    
	public MemManager(MainActivity act) {
        /**
         * Memory content file data structure:
         * [
         *   {memname1} => {create-timestamp},
         *   {memname2} => {create-timestamp},
         *   ..
         * ]
         */
		/**
         * Memory file data structure:
         * [
         *   "key1" => ["context"=>{value},"last-time"=>{time},"last-mis-count"=>{count},"S"=>{S}],
         *   "key2" => [],
         *   ..
         * ]
         */
		pathExternalFiles = act.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath();
		refreshContents();
	}

    public boolean exists(String name) {
        return getMemContents().containsKey(name);
    }

    public HashMap getMemContents() {
        return contents;
    }

    public HashMap getMemPreviews() {
        HashMap<String,HashMap> m = new HashMap();
        for(Map.Entry<String,String> entry:contents.entrySet()) {
			m.put(entry.getKey(),getMemPreview(entry.getKey()));
        }
		return m;
    }


    /**
     * MemPreview
     * [
     *      "create-time" => <Int>
     *      "example" => <String>
     *      "item-count" => <int>,
     *      "mem-keep-index" => <int>,
     * ]
     * @return
     */
    /**
     * 尤铭瑶遗忘曲线理论：
     * R = exp(-t/S)  t单位：分钟
     * S(1) = 1200/ln(c-1+e)
     * 当某次复习后，t=0，S(i+1) = S(i) * (1 + (E-1)/ln(c-1+e) )
     * E = (R-0.3)/(2*exp(7.65*(R-0.3))) -1.5R + 2.5
     * c: 该单词上次复习时的错误次数
     * R：记忆留存率   留存的记忆占初始记忆的比例
     * S：记忆质量   与遗忘速度有关，S越大，忘的越慢
     * E：复习效果   大小与目前的记忆留存率有关，在大约R=0.22时最大
     * 数字是我结合生活经验瞎掰的，不一定准确，而且人与人的体质不能一概而论
     */
    public HashMap<String,String> getMemPreview(String name) {
        HashMap<String,LinkedTreeMap> mem = getMem(name);
        HashMap<String,String> mp = new HashMap<>();
        mp.put("create-time",String.valueOf(contents.get(name)));
        int i = 0;
        String example = "";
		int averageMemKeepIndex = 0;
		Long contentsTime = Long.valueOf(contents.get(name));
		
        for(Map.Entry<String,LinkedTreeMap> entry:mem.entrySet()) {
            if(i++<2) {
                example += entry.getKey() + ", ";
            }
			Long now = entry.getValue().get("last-time").equals("0") ? contentsTime :  Long.valueOf(String.valueOf(entry.getValue().get("last-time")));
			Long deltaTime = System.currentTimeMillis() - now;
			//MainActivity.alert(""+(Long.valueOf(deltaTime)/1000.0/60));
			averageMemKeepIndex += calcIntMemKeepIndex(deltaTime/1000.0/60,Double.valueOf(String.valueOf((entry.getValue().get("S")))));
        }
		averageMemKeepIndex /= mem.size();
        mp.put("example",example);
        mp.put("item-count",String.valueOf(mem.size()));
        mp.put("mem-keep-index",String.valueOf(averageMemKeepIndex));
        return mp;
    }

    public static double calcNewMemQualty(Long t,double memQualty,int lastMis) {
        double R = Math.exp(-t/memQualty);
        double misdecline = Math.log(lastMis-1+Math.exp(1));
        double ReviewEfficiency = (R-0.3)/(2*Math.exp(7.65*(R-0.3)))-1.5*R+2.5;
        ReviewEfficiency = 1+(ReviewEfficiency-1)/misdecline;
		return (memQualty*ReviewEfficiency);
    }
     public static int calcIntMemKeepIndex(double t,double memQualty) {
        return (int)(Math.exp(-t/memQualty)*100);
     }


    public HashMap getMem(String name)  {

        File file = new File(pathExternalFiles+"/"+name+".yml");
        if (!exists(name) || !file.exists()) {
            return null;
        }
        String str = null;
        try {
            str = readExternal(pathExternalFiles+"/"+name+".yml");
        } catch (IOException e) {
            MainActivity.alert(e.toString());
        }
        if(!"".equals(str)) {
            Gson gson = new Gson();
            Object ret = gson.fromJson(str, HashMap.class);
            return (HashMap<String, LinkedTreeMap>) ret;
        }else{
            return null;
        }

    }

    public void addMem(String name,HashMap map) {
        contents.put(name,String.valueOf(System.currentTimeMillis()));
		try{
            Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
            
			//MainActivity.alert(gson.toJson(map));
            //yaml.dump(map,sw);
            writeExternal(pathExternalFiles+"/"+name+".yml",gson.toJson(map));
        }catch(Exception e){ MainActivity.alert(e.getMessage()+e.toString());}
        mapToFile();
		MainActivity.alert("New Memory in"+pathExternalFiles+"has been generated.");
    }
    public void setMem(String name,HashMap map) {
        try{
            File file = new File(pathExternalFiles+"/"+name+".yml");
            file.delete();
            Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
            writeExternal(pathExternalFiles+"/"+name+".yml",gson.toJson(map));
        }catch(Exception e){ MainActivity.alert(e.getMessage()+e.toString());}
    }
    public void dropMem(String name) {
        contents.remove(name);
		try{
			File file = new File(pathExternalFiles+"/"+name+".yml");
			if(file.delete()) {
				MainActivity.alert("Delete success");
			}else{
				MainActivity.alert("Delete fail");
			}
		}catch(Exception e){
			MainActivity.alert(e.toString());
		}
        mapToFile();
    }

    private void mapToFile() {
        try{
            Gson gson = new Gson();
            
            writeExternal(pathExternalFiles+"/contents.yml",gson.toJson(contents));
        }catch(IOException e){MainActivity.alert(e.toString());}
    }
	
	public void refreshContents() {
		try{
			File file = new File(pathExternalFiles+"/contents.yml");
			if (!file.exists()) {
				try {
					file.createNewFile();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			String str = readExternal(pathExternalFiles+"/contents.yml");
			if(!"".equals(str)) {
				Gson gson = new Gson();
        		Object ret = gson.fromJson(str,HashMap.class);
				contents = (HashMap<String,String>)ret;
			}else{
				contents = new HashMap<String,String>();
                writeExternal(pathExternalFiles+"/contents.yml","");
            }
		}catch(IOException e){
			MainActivity.alert(e.toString());
		}
	}
	
	/**
     * 从External文件目录下读取文件
     *
     * @param filePathName 要读取的文件的路径+文件名
     * @return
     * @throws IOException
     */
    public static String readExternal(String filePathName) throws IOException {
        StringBuffer stringBuffer = new StringBuffer();

        // 获取External的可用状态
        String storageState = Environment.getExternalStorageState();

        if (storageState.equals(Environment.MEDIA_MOUNTED)) {
            // 当External的可用状态为可用时

            // 打开文件输入流
            FileInputStream fileInputStream = new FileInputStream(filePathName);

            byte[] buffer = new byte[1024];
            int len = fileInputStream.read(buffer);
            // 读取文件内容
            while (len > 0) {
                stringBuffer.append(new String(buffer, 0, len));

                // 继续把数据存放在buffer中
                len = fileInputStream.read(buffer);
            }

            // 关闭输入流
            fileInputStream.close();
        }
        return stringBuffer.toString();
    }
	/**
     * 向External文件目录下写入文件
     *
     * @param filePathName 要写入的的文件的路径+文件名
     * @param content      要写入的内容
     * @throws IOException
     */
    public static void writeExternal(String filePathName, String content) throws IOException {
        // 获取External的可用状态
        String storageState = Environment.getExternalStorageState();

        if (storageState.equals(Environment.MEDIA_MOUNTED)) {
            // 当External的可用状态为可用时

            // 打开输出流
            FileOutputStream fileOutputStream = new FileOutputStream(filePathName);

            // 写入文件内容
            fileOutputStream.write(content.getBytes());

            // 关闭输出流
            fileOutputStream.close();

        }
    }
}
